/*
 * @Author: xiaosihan
 * @Date: 2024-06-23 18:33:55
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-10-10 10:02:04
 */

import BlastMesh from "@views/pc/home/component3d/BlastMesh/BlastMesh";
import { TreeDataNode } from "antd";
import { last, round } from "lodash";
import { autorun } from "mobx";
import {
  AmbientLight,
  BackSide,
  DirectionalLight,
  DoubleSide,
  EquirectangularReflectionMapping,
  LinearSRGBColorSpace,
  Material,
  Mesh,
  MeshStandardMaterial,
  Object3D,
  PointLight,
  SRGBColorSpace,
  Vector3,
} from "three";
import ThreeBase from "three-base";
import Sky from "three-base/Sky";
import threeLoader from "three-base/threeLoader";
import threeUtils from "three-base/threeUtils";
import partCollaborateStore from "../partCollaborateStore";
import commentModalStore from "./CommentModal/commentModalStore";
import Cursor from "./Cursor/Cursor";
import HeadPortraitGroup from "./HeadPortraitGroup/HeadPortraitGroup";
import { ThreeMouseEvent } from "three-base/ThreeMouse";
import NewMap from "@NewMap";
import extractMaterial from "./extractMaterial";
import ClipPlaneMesh from "./ClipPlaneMesh/ClipPlaneMesh";
import dragModelStore from "./DragModel/dragModelStore";
import profileStore from "./Profile/profileStore";

/**
 * @class HomeRender
 * @extends {ThreeBase}
 */
class Model3dRender extends ThreeBase {
  constructor() {
    super();
  }

  lightIntensity = 1.2;

  // 环境光
  ambientLight = (() => {
    const ambientLight = new AmbientLight("#fff", this.lightIntensity);
    this.scene.add(ambientLight);
    return ambientLight;
  })();

  //相机光源
  cameraPointLight = (() => {
    const cameraPointLight = new PointLight("#fff", this.lightIntensity);
    cameraPointLight.distance = 1000;
    cameraPointLight.decay = 0;
    this.scene.add(cameraPointLight);
    return cameraPointLight;
  })();

  //方向光
  directionalLight = (() => {
    const directionalLight = new DirectionalLight(
      "#ffffff",
      this.lightIntensity
    );
    directionalLight.position.set(100, 100, 1000);
    directionalLight.target.position.set(0, 0, 0);
    directionalLight.castShadow = true;
    directionalLight.shadow.mapSize.set(1024, 1024);
    this.add(directionalLight);
    return directionalLight;
  })();

  init() {
    super.init();
    this.scene.add(this.axesHelper);
    this.scene.add(this.gridHelper);
    this.axesHelper.position.set(0, 0.0001, 0);
    this.controls.enableDamping = false;
    this.controls.rotateSpeed = 1;
    this.controls.screenSpacePanning = true;
    this.axesHelper.visible = false;
    this.gridHelper.visible = false;
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.autoUpdate = true;

    // 设置全局环境贴图
    const envMap = threeLoader.getTexture("./bluesky.jpg");
    envMap.mapping = EquirectangularReflectionMapping;
    this.scene.environment = envMap;
    this.scene.environmentIntensity = 0.4;

    //扩大相机的范围
    // this.perspectiveCamera.near = 1;
    this.perspectiveCamera.far = 50000;
    this.perspectiveCamera.updateProjectionMatrix();

    this.resetLookAt({
      center: new Vector3(0, 0, 0),
      position: new Vector3(100, 100, 100),
    });
  }

  // 鼠标样式
  cursor = (() => {
    const cursor = new Cursor();
    cursor.visible = false;
    this.scene.add(cursor);
    return cursor;
  })();

  // 天空球
  sky = (() => {
    const sky = new Sky();
    sky.material.side = BackSide;
    sky.scale.multiplyScalar(5);
    //three事件
    sky.userData = {
      cursor: "pointer",
      enableEvent: true,
    };

    sky.addEventListener("mousemove", (e) => {
      //@ts-ignore
      this.cursor.position.copy(e.point as Vector3);
    });

    sky.addEventListener("click", (e) => {
      partCollaborateStore.setShowRightMenu(false);
    });

    sky.addEventListener("leftclick", (e) => {
      const meshs: Array<Mesh> = [];
      this.blastMesh.traverseMesh((mesh) => {
        if (mesh.visible) {
          meshs.push(mesh);
        }
      });
      let intersection = this.threeMouse._raycaster.intersectObjects(
        meshs,
        false
      )[0];

      if (intersection) {
        intersection.object.dispatchEvent({
          ...e,
          ...intersection,
        } as never);
      } else {
        const { showSingleObject } = partCollaborateStore;
        if (showSingleObject) {
        } else {
          partCollaborateStore.setActiveId();
        }
        commentModalStore.setShow(false);
        partCollaborateStore.setSelectModel(false);
      }
    });

    //左键点击事件
    // sky.addEventListener("leftclick", (e) => {
    //   partCollaborateStore.setShowRightMenu(false);
    // });
    //右键点击事件
    sky.addEventListener("rightclick", (event: any) => {
      const e = event as ThreeMouseEvent;
      partCollaborateStore.setShowRightMenu(true);
      const { offsetX: x, offsetY: y } = e;
      partCollaborateStore.setRightMenuPosition({ x, y });
    });

    sky.material.map = null;
    sky.material.color.set("#242136");
    this.scene.add(sky);
    return sky;
  })();

  // 头像标签组
  headPortraitGroup = (() => {
    const headPortraitGroup = new HeadPortraitGroup();
    this.scene.add(headPortraitGroup);
    return headPortraitGroup;
  })();

  async loadModel(url: string) {
    partCollaborateStore.setLoadding(true);
    const { modelId } = partCollaborateStore;
    // await threeLoader.loadModel(url);
    if (modelId === partCollaborateStore.modelId) {
      await this.blastMesh.loadModel(url);
    } else {
    }
    partCollaborateStore.setLoadding(false);
  }

  originOpcityMap = new NewMap<string, number>();
  originTransparentMap = new NewMap<string, boolean>();

  // 裁剪面模型
  clipPlaneMesh = (() => {
    const clipPlaneMesh = new ClipPlaneMesh(
      this.getCamera(),
      this.renderer.domElement
    );
    let originContorlEnabled = false;
    clipPlaneMesh.controls.addEventListener("mouseDown", () => {
      originContorlEnabled = this.controls.enabled;
      this.controls.enabled = false;
    });
    clipPlaneMesh.controls.addEventListener("mouseUp", () => {
      this.controls.enabled = originContorlEnabled;
    });
    this.add(clipPlaneMesh);
    return clipPlaneMesh;
  })();

  //监听裁剪状态的改变
  dispose7 = autorun(
    () => {
      const { showList } = dragModelStore;
      const { profileDirection, profileSide } = profileStore;
      this.clipPlaneMesh.set({
        visible: showList.includes("pro_file"),
        direction: profileDirection,
        profileSide,
      });
      this.renderer.localClippingEnabled = showList.includes("pro_file");
    },
    { delay: 30 }
  );

  //爆炸模型
  blastMesh = (() => {
    const blastMesh = new BlastMesh();
    blastMesh.castShadow = true;
    blastMesh.receiveShadow = true;
    blastMesh.addEventListener("loaded", () => {
      const { x, y, z } = blastMesh.size;
      this.clipPlaneMesh.setSize(x, y, z);

      this.originOpcityMap.clear();
      this.originTransparentMap.clear();

      const materials = extractMaterial(
        this.blastMesh
      ) as Array<MeshStandardMaterial>;
      materials.map((material) => {
        this.originOpcityMap.set(material.uuid, material.opacity);
        this.originTransparentMap.set(material.uuid, material.transparent);
      });

      // 修改材质
      blastMesh.traverseMesh((mesh) => {
        mesh.frustumCulled = false;
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        const materials = extractMaterial(mesh);
        materials.map((m) => {
          m.clippingPlanes = [this.clipPlaneMesh.plane];
        });
      });

      //保存每个模型对象的爆炸方向
      const blastDirection: Array<{ x: number; y: number; z: number }> = [];

      blastMesh.blastGroup.children.map((o, i) => {
        const mesh = o as Mesh;

        //three事件
        mesh.userData = {
          cursor: "pointer",
          enableEvent: false,
          stroke_enable: false, //启用描边
          stroke_color: "#04fa12", //描边颜色
          stroke_width: 1, //描边厚度(像素)
        };

        mesh.addEventListener("leftclick", (e: any) => {
          const { addMark } = partCollaborateStore;
          if (addMark) {
            const sdie = blastMesh.size.length() / 40;

            (e.normal as Vector3).multiplyScalar(sdie);
            const x = round(e.point.x + e.normal.x, 4);
            const y = round(e.point.y + e.normal.y, 4);
            const z = round(e.point.z + e.normal.z, 4);
            partCollaborateStore.setAddMark(false);
            // 打开评论弹窗
            commentModalStore.setShow(true);
            commentModalStore.setModelIndex(i);
            commentModalStore.setVPosition({ x, y, z });
            commentModalStore.setPlaceholder("请输入评论");
          } else {
            partCollaborateStore.setActiveId([mesh.uuid]);
            partCollaborateStore.setSelectModel(false);
          }
        });

        mesh.addEventListener("mousemove", (e) => {
          //@ts-ignore
          this.cursor.position.copy(e.point as Vector3);
        });

        const center = threeUtils.getCenter(o);
        blastDirection.push({ x: center.x, y: center.y, z: center.z });
      });

      // 设置每个模型的爆炸方向
      partCollaborateStore.setBlastDirection(blastDirection);

      const size = threeUtils.getSize(blastMesh);

      partCollaborateStore.setModalSize({ x: size.x, y: size.y, z: size.z });

      this.lookAtDefault();

      // 设置模型的层级结构
      const modelLyaer = this.getModelLyaer(blastMesh.blastGroup);
      partCollaborateStore.setModelLyaer(modelLyaer);
      partCollaborateStore.setSelectModel(true);
    });

    this.scene.add(blastMesh);

    return blastMesh;
  })();

  //获取模型层级结构数组
  getModelLyaer(object: Object3D): Array<TreeDataNode & { isMesh: boolean }> {
    const modelLyaer: Array<TreeDataNode & { isMesh: boolean }> = [];
    object.children.map((o) => {
      modelLyaer.push({
        title: o.name || o.uuid.slice(0, 5),
        key: o.uuid,
        isMesh: (o as Mesh).isMesh,
        children: this.getModelLyaer(o),
      });
    });
    return modelLyaer;
  }

  //设置左键拖动
  setLeftKeyDrop() {
    this.setControlType("MapControls");
  }

  //设置左键旋转
  setLeftRotation() {
    this.setControlType("OrbitControls");
  }

  //x视角
  lookAtX(x: number = 1) {
    this.lookatObjects({
      objects: [this.blastMesh],
      duration: 500,
      direction: new Vector3(x, 0, 0),
      offset: new Vector3(0, 0, 0),
      distance: 1.5,
    });
  }

  //y视角
  lookAtY() {
    this.lookatObjects({
      objects: [this.blastMesh],
      duration: 500,
      direction: new Vector3(0.001, 3.169, 0.05),
      offset: new Vector3(0, 0, 0),
      distance: 1.5,
    });
  }

  //z视角
  lookAtZ() {
    this.lookatObjects({
      objects: [this.blastMesh],
      duration: 500,
      direction: new Vector3(0, 0, 1),
      offset: new Vector3(0, 0, 0),
      distance: 1.5,
    });
  }

  //默认视角
  lookAtDefault() {
    this.lookatObjects({
      objects: [this.blastMesh],
      duration: 500,
      direction: new Vector3(1, 0.5, 1),
      offset: new Vector3(0, 0, 0),
      distance: 1.5,
    });
  }

  // 监听是否添加点事件
  dispose = autorun(() => {
    const { addMark, selectModel, controlType } = partCollaborateStore;
    this.setControlType(controlType);
    this.blastMesh.blastGroup.traverse((o) => {
      // if (addMark) {
      //     Object.assign(o.userData, {
      //         enableEvent: true,
      //         cursor: "none",
      //     });
      // } else if (selectModel) {
      //     Object.assign(o.userData, {
      //         enableEvent: true,
      //         cursor: "default",
      //     });
      // } else {
      //     Object.assign(o.userData, {
      //         enableEvent: false
      //     });
      // }
    });

    this.cursor.position.set(9999999, 9999999, 9999999);
    this.cursor.visible = addMark;
    if (addMark) {
      Object.assign(this.sky.userData, {
        cursor: "none",
      });
    } else {
      Object.assign(this.sky.userData, {
        cursor: "default",
      });
      // } else {
      // const cursorConf = {
      //     "MapControls": "grabbing",
      //     "OrbitControls": "all-scroll"
      // }
      // Object.assign(this.sky.userData, {
      //     cursor: cursorConf[controlType],
      // });
    }
  });

  //监听爆炸范围事件
  dispose1 = autorun(() => {
    const { blastDirection, blastProgress } = partCollaborateStore;
    this.blastMesh.setBlastProgress(blastProgress);
    this.headPortraitGroup.children.map((o) => {
      if (blastDirection[o.meshIndex]) {
        const { x, y, z } = blastDirection[o.meshIndex];
        o.position
          .set(x, y, z)
          .multiplyScalar(blastProgress)
          .add(o.originPosition);
      }
    });
  });

  //选中状态的监听
  dispose2 = autorun(
    () => {
      const { activeId, showSingleObject, hideIds } = partCollaborateStore;

      const id = last(activeId);

      this.blastMesh.blastGroup.traverse((o) => {
        const mesh = o as Mesh;
        if (mesh.isMesh) {
          if (hideIds.includes(mesh.uuid)) {
            mesh.visible = false;
          } else if (!showSingleObject) {
            mesh.visible = true;
          } else if (showSingleObject) {
            if (mesh.uuid === id) {
              mesh.visible = true;
            } else {
              mesh.visible = false;
            }
          }

          Object.assign(mesh.userData, {
            cover_enable: mesh.uuid === id, //是否叠加颜色
            cover_color: "#5503FA", //叠加颜色
            cover_opacity: 0.4, //颜色叠加透明度
          });
        }
      });
    },
    { delay: 10 }
  );

  //监听是否只显示单个模型
  dispose5 = autorun(
    async () => {
      const { showSingleObject } = partCollaborateStore;
      await new Promise((resolve) => setTimeout(resolve, 100));
      const { activeId } = partCollaborateStore;
      const id = last(activeId);
      this.blastMesh.blastGroup.traverse((o) => {
        const mesh = o as Mesh;
        if (mesh.isMesh) {
          if (showSingleObject) {
            if (mesh.uuid === id) {
              mesh.visible = true;
              const { position } = this.getLookAt();
              const center = threeUtils.getCenter(mesh);
              this.setLookAt({ position, center, duration: 200 });
            }
          }
        }
      });

      if (!showSingleObject) {
        const { position } = this.getLookAt();
        const center = threeUtils.getCenter(this.blastMesh);
        this.setLookAt({ position, center, duration: 200 });
      }
    },
    { delay: 10 }
  );

  //监听透明度的变化
  dispose4 = autorun(
    () => {
      const { transparent } = partCollaborateStore;
      const materials = extractMaterial(
        this.blastMesh
      ) as Array<MeshStandardMaterial>;
      materials.map((material) => {
        const originOpcity = this.originOpcityMap.getOrSet(
          material.uuid,
          material.opacity
        )!;
        material.opacity = transparent ? 0.5 * originOpcity : originOpcity;
        material.transparent =
          transparent ||
          this.originTransparentMap.getOrSet(
            material.uuid,
            material.transparent
          ) ||
          false;
        material.needsUpdate = true;
      });
    },
    { delay: 10 }
  );

  //加载时不显示 标签组
  dispose3 = autorun(() => {
    const { loadding, hideLable } = partCollaborateStore;
    this.headPortraitGroup.visible = !loadding && !hideLable;
  });

  centerMap = new NewMap<string, Vector3>();
  radiusMap = new NewMap<string, number>();
  render() {
    //设置光源的位置
    const position = this.getCamera().position.clone();
    const center = this.controls.target.clone();
    this.cameraPointLight.position.copy(position);

    //计算每个mesh的渲染顺序
    const meshCenter = this.centerMap.getOrSet(
      this.blastMesh.uuid,
      threeUtils.getCenter(this.blastMesh)
    )!;
    const D = this.getCamera().position.distanceTo(meshCenter) * 10;
    this.blastMesh.blastGroup.traverse((o) => {
      const mesh = o as Mesh;
      if (mesh.isMesh) {
        const center = this.centerMap.getOrSet(
          mesh.uuid,
          threeUtils.getCenter(mesh)
        )!;
        const distance = this.getCamera().position.distanceTo(center);
        const radius = this.radiusMap.getOrSetCall(mesh.uuid, (id: string) => {
          mesh.geometry.computeBoundingSphere();
          const { radius } = mesh.geometry.boundingSphere!;
          return radius;
        })!;
        mesh.renderOrder = D - radius - distance;
      }
    });

    super.render();

    if (commentModalStore.show) {
      const { vPosition } = commentModalStore;
      this.tempVector3.set(vPosition.x, vPosition.y, vPosition.z);
      const { x, y } = this.v3tov2(this.tempVector3);
      commentModalStore.setPosition({ x, y });
    }
  }
}

const model3dRender = (window.model3dRender = new Model3dRender());

export default model3dRender;
